package com.gxgeek.rule;

import com.alibaba.fastjson.JSON;
import com.gxgeek.CoreConstants;
import com.gxgeek.dto.Rule;
import com.gxgeek.dto.RuleCondition;
import com.gxgeek.dto.RuleConditionParam;
import com.gxgeek.entity.RiskConditionsEntity;
import com.gxgeek.entity.RiskRuleCondsEntity;
import com.gxgeek.entity.RiskRulesEntity;
import com.gxgeek.enums.RuleStatusEnums;
import com.gxgeek.repository.RiskCondParamsRepository;
import com.gxgeek.repository.RiskConditionsRepository;
import com.gxgeek.repository.RiskRuleCondsRepository;
import com.gxgeek.repository.RiskRulesRepository;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.StopWatch;

import java.io.IOException;
import java.rmi.ServerException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.gxgeek.ScriptUtils.loadGroovyScript;
import static java.util.stream.Collectors.toList;

/**
 * 加载规则脚本Service
 * @author gaoxing
 * @create 2018-05-22 下午4:49
 */
@Slf4j
@Service
public class RiskLoadRuleService implements CommandLineRunner{
    @Autowired
    RiskRulesRepository riskRulesRepository;
    @Autowired
    RiskRuleCondsRepository riskRuleCondsRepository;
    @Autowired
    RiskConditionsRepository riskConditionsRepository;
    @Autowired
    RiskCondParamsRepository riskCondParamsRepository;


    @Autowired
    Configuration freemarkerConfiguration;

    //规则ID->规则信息
    private Map<Long, Rule> ruleLists = new HashMap<>();

    @Override
    public void run(String... args) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        loadRule();
        stopWatch.stop();
        log.info("加载规则所需时间:{}s",stopWatch.getTotalTimeSeconds());
    }


    private void loadRule() {
        //找出启用的规则
        ruleLists.clear();
        List<RiskRulesEntity> riskRulesEntityList = riskRulesRepository.findByRuleStatus(RuleStatusEnums.ON.name());
        HashMap<Long, Rule> collect = riskRulesEntityList.stream().collect(
                HashMap<Long, Rule>::new,
                (map, s) -> map.put(s.getRuleId(), getRule(s)),
                HashMap::putAll);
        ruleLists.putAll(collect);
    }

    private Rule getRule(RiskRulesEntity riskRulesEntity) {
        Rule rule = new Rule();

        //找出所有 规则下的条件
        List<RiskRuleCondsEntity> ruleCondsList = riskRuleCondsRepository.findByRuleId(riskRulesEntity.getRuleId());
        List<RuleCondition> ruleCondition = ruleCondsList.stream()
                                                         .map(this::getCondition)
                                                         .collect(toList());
        rule.setConditions(ruleCondition);
        rule.setCondExpress(riskRulesEntity.getCondExpress());
        rule.setNotifyPolicy(riskRulesEntity.getNotifyPolicy());
        rule.setRuleCode(riskRulesEntity.getRuleCode());
        rule.setRuleScore(riskRulesEntity.getRuleScore());
        rule.setRuleId(riskRulesEntity.getRuleId());
        return rule;
    }


    private RuleCondition getCondition(RiskRuleCondsEntity riskRuleCondsEntity) {
        RuleCondition condition = new RuleCondition();
        Optional<RiskConditionsEntity> riskConditionsEntity = riskConditionsRepository
                                                                .findById(riskRuleCondsEntity.getCondId());
        if (!riskConditionsEntity.isPresent()){
            throw new RuntimeException("规则无条件Id");
        }
        try {
            String beanName = CoreConstants.Decision.SCRIPT_NAME_PREFIX + riskConditionsEntity.get().getId();
            Class GroovyClass = loadGroovyScript(getGroovyCode(riskConditionsEntity.get()), beanName);
            GroovyObject groovy = (GroovyObject) GroovyClass.newInstance();
            condition.setGroovy(groovy);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            throw new RuntimeException("script" + riskConditionsEntity.get().getId());
        }
        condition.setCondId(riskRuleCondsEntity.getCondId());
        condition.setCondParams(JSON.parseObject(riskRuleCondsEntity.getCondParams(), HashMap.class));
        condition.setId(riskConditionsEntity.get().getId());

        List<RuleConditionParam> paramList = riskCondParamsRepository.findByCondId(riskConditionsEntity.get().getId())
                .stream()
                .map((s) -> RuleConditionParam.builder()
                        .condDimesion(s.getDimension()).build())
                .collect(toList());
        condition.setCondDimesions(paramList);
        return condition;
    }

    //模板引擎生成代码
    private String getGroovyCode(RiskConditionsEntity riskConditionsEntity) throws IOException, TemplateException {
        Map<String, Object> model = new HashMap();
        model.put("className", CoreConstants.Groovy.GROOVY_METHOD_NAME);
        model.put("script", riskConditionsEntity.getJudgeScript());
        String text = FreeMarkerTemplateUtils.processTemplateIntoString( freemarkerConfiguration.getTemplate("groovyClass.ftl"),  model);
        return text;
    }







    public Rule findRuleByRuleId(Long ruleId){
        Rule rule = ruleLists.get(ruleId);
        if (rule==null){
            throw new RuntimeException("该规则不存在");
        }
        return rule;
    }

    public List<String> findRulesParams(Long ruleId){
        Rule rule = findRuleByRuleId(ruleId);
        List<String> paramList = rule.getConditions()
                .stream()
                .flatMap(s -> s.getCondDimesions().stream())
                .map(RuleConditionParam::getCondDimesion)
                .collect(toList());
        return paramList;
    }

}
